package com.easygem.front.service.imp;

import com.easygem.common.model.front.ResourceInfo;
import com.easygem.common.model.front.TaskPackage;
import com.easygem.common.model.front.TaskPackageResource;
import com.easygem.common.response.ActiveUser;
import com.easygem.common.vo.front.ResourceInfoVO;
import com.easygem.common.vo.front.TaskPackageResourceVO;
import com.easygem.common.vo.front.TaskPackageVO;
import com.easygem.common.vo.system.PageVO;
import com.easygem.front.mapper.DepartmentMapper;
import com.easygem.front.mapper.ResourceInfoMapper;
import com.easygem.front.mapper.TaskPackageMapper;
import com.easygem.front.mapper.TaskPackageResourceMapper;
import com.easygem.front.service.TaskPackageResourceService;
import com.easygem.system.mapper.UserMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.Objects;
import java.util.HashSet;
import java.util.Set;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @author 李鹏志
 * @version 1.0
 * @Description TODO
 * @date 2024/12/23 16:38
 */
@Service
@Transactional
@Slf4j
public class TaskPackageResourceServiceImpl implements TaskPackageResourceService {
    @Autowired
    private TaskPackageResourceMapper taskPackageResourceMapper;
    @Autowired
    private DepartmentMapper departmentMapper;
    @Autowired
    private ResourceInfoMapper resourceInfoMapper;
    @Autowired
    private TaskPackageMapper taskPackageMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PlatformTransactionManager transactionManager;

    // 从配置文件读取批次大小，提供默认值
    @Value("${import.batch.size:500}")
    private int batchSize = 500;
    
    // 线程池大小，默认值
    @Value("${import.thread.pool.size:10}")
    private int threadPoolSize = 10;

    /**
     * 根据任务包id删除任务包资源
     *
     * @param resourceIds 任务包id
     * @return 删除的数量
     */
    @Override
    public int delByTPResourceId(List<Long> resourceIds) {
        return taskPackageResourceMapper.delByTPResourceId(resourceIds);
    }

    /**
     * 批量分配资源
     *
     * @param batchAllocationMap 任务包id和资源id列表
     * @return 分配的数量
     */
    @Override
    @Transactional
    public int batchAllocationResource(Map<String, List<Long>> batchAllocationMap, Integer taskPackageId) {
        // 获取资源编号
        List<Long> resourceIds = batchAllocationMap.get("resourceIds");
        // 获取用户编号
        List<Long> userIds = batchAllocationMap.get("userIds");

        int userSize = userIds.size();
        int resourceSize = resourceIds.size();

        int allocatedCount = 0; // 记录已分配的资源数量
        while (allocatedCount < resourceSize) {
            // 每次分配时，确定本次要分配的资源范围（考虑剩余未分配资源）
            int endIndex = Math.min(allocatedCount + userSize, resourceSize);
            List<Long> subResourceList = resourceIds.subList(allocatedCount, endIndex);

            // 调用数据访问层方法进行本次分配
            int updateResult = taskPackageResourceMapper.batchAllocationResource(userIds, subResourceList, taskPackageId);
            if (updateResult <= 0) {
                // 如果本次分配失败（更新行数小于等于0），可以根据业务需求进行相应处理，比如抛出异常或者记录日志等
                // 这里简单返回，表示分配出现问题
                return updateResult;
            }

            // 更新已分配的资源数量
            allocatedCount += subResourceList.size();
        }
        return allocatedCount;
    }

    /**
     * 回收资源
     *
     * @param resourceIds 资源id列表
     * @return 回收的数量
     */
    @Override
    @Transactional
    public int recycleResources(List<Long> resourceIds) {
        //调用查询当前用户部门id方法
//        ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipal();
//        List<Long> ids= DepUtils.getDepartmentAndChildrenIds(departmentMapper.selectAll());
        return taskPackageResourceMapper.recycleResources(resourceIds);
    }

    /**
     * 查询已分配的资源
     *
     * @param pageNum
     * @param pageSize
     * @param tPResourceVO 任务包资源
     * @return 已分配的资源列表
     */
    @Override
    public PageVO<TaskPackageResource> getAllAssignedResources(Integer pageNum, Integer pageSize, TaskPackageResourceVO tPResourceVO) {
        //获取当前登录用户对象
//        ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipal();
//
//        if (activeUser.getUser().getType()!=2){
//            List<Long> ids= DepUtils.getDepartmentAndChildrenIds(departmentMapper.selectAll(),activeUser.getUser().getDeptId());
//            tPResourceVO.setIds(ids);
//        }else {
//            tPResourceVO.setUserId(activeUser.getUser().getUserId());
//        }
        PageHelper.startPage(pageNum,pageSize);
        TaskPackageResource tPResource = new TaskPackageResource();
        //存入当前登录用户的账户名
//        tPResourceVO.setAccountName(activeUser.getUser().getAccountName());
        //查询条件
        BeanUtils.copyProperties(tPResourceVO,tPResource);
        //查询所有任务资源
        List<TaskPackageResource> tPResourceList = taskPackageResourceMapper.getAllAssignedResources(tPResource);
        PageInfo<TaskPackageResource> info = new PageInfo<>(tPResourceList);
        return new PageVO<>(info.getTotal(),info.getList());
    }

    /**
     * 查询未分配的资源
     *
     * @param pageNum
     * @param pageSize
     * @param tPResourceVO 任务包资源
     * @return 未分配的资源列表
     */
    @Override
    public PageVO<TaskPackageResource> getNotAssignedResources(Integer pageNum, Integer pageSize, TaskPackageResourceVO tPResourceVO) {
        //获取当前登录用户对象
//        ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipal();
//        if (activeUser.getUser().getType()!=2){
//            List<Long> ids= DepUtils.getDepartmentAndChildrenIds(departmentMapper.selectAll(),activeUser.getUser().getDeptId());
//            tPResourceVO.setIds(ids);
//        }else {
//            tPResourceVO.setUserId(activeUser.getUser().getUserId());
//        }
        PageHelper.startPage(pageNum,pageSize);
        TaskPackageResource tPResource = new TaskPackageResource();
        //存入当前登录用户的账户名
        //tPResourceVO.setAccountName(activeUser.getUser().getAccountName());
        //查询条件
        BeanUtils.copyProperties(tPResourceVO,tPResource);
        //查询所有任务资源
        List<TaskPackageResource> tPResourceList = taskPackageResourceMapper.getNotAssignedResources(tPResource);
        PageInfo<TaskPackageResource> info = new PageInfo<>(tPResourceList);
        return new PageVO<>(info.getTotal(),info.getList());
    }

    /**
     * 导出任务包资源
     * @param resourceIds 资源id列表
     * @return 资源列表
     */
    @Override
    public List<TaskPackageResource> downTPExcel(List<Long> resourceIds) {
        return taskPackageResourceMapper.downTPExcel(resourceIds);
    }

    /**
     * 根据任务包id查询所有资源
     *
     * @param taskPackageId 任务包id
     * @return 资源列表
     */
    @Override
    public PageVO<TaskPackageResource> getAllResources(Integer pageNum, Integer pageSize,Integer taskPackageId) {
        PageHelper.startPage(pageNum,pageSize);
        List<TaskPackageResource> tPResourceList = taskPackageResourceMapper.getAllResources(taskPackageId);
        PageInfo<TaskPackageResource> info = new PageInfo<>(tPResourceList);
        return new PageVO<>(info.getTotal(),info.getList());
    }


    /**
     * 导入资源
     *
     * @param resourceInfos 资源
     * @param taskPackageId 任务包id
     * @return 导入的数量
     */
    @Override
    @Transactional
    public String importResource(List<ResourceInfoVO> resourceInfos, Integer taskPackageId,String accountName) throws Exception {
        //创建用于报错的字符串
        String context = "success";
        // 参数校验
        if (resourceInfos == null || resourceInfos.isEmpty() || taskPackageId == null) {
            return "您导入的数据与已有资源重复,操作已结束";
        }

        // 获取当前用户信息
        ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipal();

        // 配置线程池最大线程数量为2
        int poolSize = 2;
        ExecutorService executor = Executors.newFixedThreadPool(poolSize,
                r -> new Thread(r, "resource-import-" + ThreadLocalRandom.current().nextInt(1000)));

        // 配置事务模板
        final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        transactionTemplate.setTimeout(30);

        try {
            // 分批处理
            final int batchSize = 500;
            List<Future<?>> futures = new ArrayList<>();

            for (int i = 0; i < resourceInfos.size(); i += batchSize) {
                final int start = i;
                final int end = Math.min(i + batchSize, resourceInfos.size());
                final List<ResourceInfoVO> batch = resourceInfos.subList(start, end);

                futures.add(executor.submit(() -> processBatch(
                        batch, taskPackageId, accountName, transactionTemplate)));
            }

            // 检查执行结果
            for (Future<?> future : futures) {
                future.get(); // 如有异常会在此抛出
            }
            return context;
        } finally {
            executor.shutdown();
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        }
    }


    /**
     * 处理单个批次
     */
    private Void processBatch(List<ResourceInfoVO> batch, Integer taskPackageId,
                              String accountName, TransactionTemplate transactionTemplate) {
        return transactionTemplate.execute(status -> {
            List<ResourceInfo> entities = new ArrayList<>(batch.size());
            List<String> phones = new ArrayList<>(batch.size());

            for (ResourceInfoVO vo : batch) {
                // 手机号验证
                if (vo.getPhoneA() == null || vo.getPhoneA().length() < 11 || vo.getPhoneA().length() > 12) {
                    continue;
                }

                // 实体转换
                ResourceInfo entity = new ResourceInfo();
                BeanUtils.copyProperties(vo, entity);
                if (accountName!=null){
                    entity.setAccountName(accountName);
                }
                entity.setResourceTag(0); // 设置为未标记
                entity.setIsCall(0); // 设置为未呼叫
                entity.setIsDelete(0); // 设置为未删除
                entities.add(entity);
                phones.add(vo.getPhoneA());

                // 处理重复手机号
                if (vo.getPhoneA().equals(vo.getPhoneB())) {
                    vo.setPhoneB(null);
                }
            }

            if (!entities.isEmpty()) {

                flushBatch(entities, phones, taskPackageId);
            }
            return null;
        });
    }

    /**
     *  批量插入资源表（自动去重）
     * @param batchList 资源列表
     * @param phoneBatch 手机号列表
     * @param taskPackageId 任务包id
     */
    @Synchronized
    private void flushBatch(List<ResourceInfo> batchList, List<String> phoneBatch, Integer taskPackageId) {
        if (batchList.isEmpty()) return;
        // 批量插入资源表
        resourceInfoMapper.batchAddResourceInfo(batchList);

        // 批量关联任务包
        taskPackageResourceMapper.addTaskPackageResourceByPhones(phoneBatch, taskPackageId);

        // 清空批次
        batchList.clear();
        phoneBatch.clear();
    }

    /**
     * 创建任务包
     *
     * @param taskPackageVO 任务包对象
     * @return 创建的数量
     */
    @Override
    @Transactional
    public String createTaskPackage(List<ResourceInfoVO> resourceInfos,TaskPackageVO taskPackageVO)throws Exception{
        //创建用于报错的字符串
        String context = "success";
        // 参数校验
        if (resourceInfos == null || resourceInfos.isEmpty() || taskPackageVO == null) {
            return "您导入的数据与已有资源重复,操作已结束";
        }
        //获取当前登录的用户id
//        ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipal();
//        Long userId = activeUser.getUser().getUserId();
        //将当前登录的用户id赋值给创建人id
//        taskPackageVO.setUserId(userId);
        //将VO转成对象
        TaskPackage taskPackage = new TaskPackage();
        BeanUtils.copyProperties(taskPackageVO,taskPackage);
        //调用创建任务包方法
        taskPackageMapper.addTaskPackage(taskPackage);
        //获取创建的任务包编号
        Integer taskPackageId = taskPackage.getTaskPackageId();
        //调用根据创建的任务包名查询任务包编号方法
        //Integer taskPackageId = taskPackageMapper.getTaskPackageIdByName(taskPackage.getTaskPackageName(),activeUser.getUser().getAccountName());
        //调用导入资源方法
        context = importResource(resourceInfos,taskPackageId,taskPackageVO.getAccountName());
        return context;
    }

    /**
     * 根据手机号查重
     *
     * @param taskPackageIds 传入的任务包编号（用来判断查重方式）
     * @return 处理的数量
     */
    @Override
    @Transactional
    public Integer findDuplicatePhones(List<Integer> taskPackageIds) {
        return taskPackageResourceMapper.findDuplicatePhones(taskPackageIds);
    }

    /**
     * 资源回收
     *
     * @param taskPackageVO 任务包对象
     * @return 操作状态
     */
    @Override
    @Transactional
    public String resourceRecycling(TaskPackageVO taskPackageVO,
                                    Long oldUserId,
                                    Long newUserId) {
        //1、根据用户编号获取任务包编号
        //List<Long> taskPackageIds = taskPackageMapper.getTaskPackageIdByUserIds(userId);
        //2、根据任务包编号查询任务包资源表中的资源编号及任务包资源编号
        //List<TaskPackageResource> taskPackageResourceList = taskPackageResourceMapper.getTaskPackageResourceByTaskPackageIds(taskPackageIds);
        //3、根据原任务资源编号删除任务资源表记录
        //List<Long> resourceIds = taskPackageResourceList.stream().map(TaskPackageResource::getResourceId).collect(Collectors.toList());

        //1、根据归属人查询资源编号
        List<Long> resourceIds = resourceInfoMapper.getResourceIdByUserIds(oldUserId);
        if (resourceIds.size() <= 0) {
            return "isNull";
        }
        //根据编号改变资源状态(清空资源归属人)
        resourceInfoMapper.updateResourceTagByResourceId(resourceIds);
        //根据资源编号查询任务资源表中是否存在该资源的数据

        //执行删除任务资源记录方法
        taskPackageResourceMapper.delByTPResourceIds(resourceIds);
        //4、创建新的任务包并获取新任务包的编号
        //获取当前登录的用户id
//        ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipal();
//        Long tpUserId = activeUser.getUser().getUserId();
        //将VO转成对象
        TaskPackage taskPackage = new TaskPackage();
        BeanUtils.copyProperties(taskPackageVO,taskPackage);
        //将当前登录的用户id赋值给创建人id
        taskPackage.setUserId(newUserId);
        //调用创建任务包方法
        taskPackageMapper.addTaskPackage(taskPackage);
        //获取创建的任务包编号
        Integer taskPackageId = taskPackage.getTaskPackageId();
        //5、根据新建任务包的编号和查出来的资源编号创建任务资源表记录
        taskPackageResourceMapper.addTaskPackageResource(resourceIds,taskPackageId);
        return "success";
    }

    /**
     * 客户回收
     *
     * @param taskPackageVO 任务包对象
     * @return 操作状态
     */
    @Override
    @Transactional
    public String customerRecycling(TaskPackageVO taskPackageVO,
                                    Long oldUserId,
                                    Long newUserId) {
        // 0. 增加分页批量处理机制
        final int BATCH_SIZE = 500;
        // 1. 查询资源ID（优化为分批次查询）
        List<Long> resourceIds = resourceInfoMapper.getCustomerIdByUserIds(oldUserId);
        if (resourceIds.size() <= 0) {
            return "isNull";
        }
        // 2、 批量更新资源状态（分批次更新）
        if (!resourceIds.isEmpty()) {
            List<List<Long>> resourceIdBatches = Lists.partition(resourceIds, BATCH_SIZE);
            for (List<Long> batch : resourceIdBatches) {
                resourceInfoMapper.updateResourceTagByResourceId(batch);
            }
        }
        //执行删除任务资源记录方法
        taskPackageResourceMapper.delByTPResourceIds(resourceIds);
        // 3. 创建任务包（保持原逻辑）
//        ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipal();
//        Long tpUserId = activeUser.getUser().getUserId();

        TaskPackage taskPackage = new TaskPackage();
        BeanUtils.copyProperties(taskPackageVO, taskPackage);
        taskPackage.setUserId(newUserId);
        taskPackageMapper.addTaskPackage(taskPackage);
        Integer taskPackageId = taskPackage.getTaskPackageId();

        // 4. 批量插入任务资源（分批次插入）
        if (!resourceIds.isEmpty()) {
            List<List<Long>> resourceIdBatches = Lists.partition(resourceIds, BATCH_SIZE);
            for (List<Long> batch : resourceIdBatches) {
                taskPackageResourceMapper.addTaskPackageResource(batch, taskPackageId);
            }
        }

        return "success";
    }

    /**
     * 导入统计信息类，用于记录和反馈导入进度
     */
    public static class ImportStats {
        private long uploadStartTime; // 上传开始时间
        private long uploadEndTime; // 上传结束时间
        private long importStartTime; // 导入开始时间
        private long importEndTime; // 导入结束时间
        private int totalRows; // 文件总行数
        private int processedRows; // 已处理行数
        private int importedRows; // 成功导入行数
        private int completedBatches; // 已完成批次
        private int totalBatches; // 总批次数
        private String status; // 当前状态
        private String errorMessage; // 错误信息
        private final int batchSizeValue; // 批次大小

        public ImportStats(int batchSize) {
            this.uploadStartTime = System.currentTimeMillis();
            this.status = "uploading"; // 初始状态为上传中
            this.batchSizeValue = batchSize;
        }

        public void startImport(int totalRows) {
            this.uploadEndTime = System.currentTimeMillis();
            this.importStartTime = System.currentTimeMillis();
            this.totalRows = totalRows;
            this.totalBatches = (totalRows + batchSizeValue - 1) / batchSizeValue;
            this.status = "importing";
        }

        public void updateProgress(int processedInBatch, int importedInBatch) {
            this.processedRows += processedInBatch;
            this.importedRows += importedInBatch;
            this.completedBatches++;
        }

        public void completeImport() {
            this.importEndTime = System.currentTimeMillis();
            this.status = "completed";
        }

        public void setError(String message) {
            this.errorMessage = message;
            this.status = "error";
            this.importEndTime = System.currentTimeMillis();
        }
        
        public void setPartialError(String message) {
            this.errorMessage = message;
            this.status = "partial_error"; // 部分成功
            this.importEndTime = System.currentTimeMillis();
        }

        public Map<String, Object> toMap() {
            Map<String, Object> result = new HashMap<>();
            result.put("uploadTimeSeconds", (uploadEndTime - uploadStartTime) / 1000.0);
            result.put("importTimeSeconds", ((importEndTime > 0 ? importEndTime : System.currentTimeMillis()) - importStartTime) / 1000.0);
            result.put("totalRows", totalRows);
            result.put("processedRows", processedRows);
            result.put("importedRows", importedRows);
            result.put("progress", totalRows > 0 ? (double) processedRows / totalRows * 100 : 0);
            result.put("completedBatches", completedBatches);
            result.put("totalBatches", totalBatches);
            result.put("status", status);
            if (errorMessage != null) {
                result.put("errorMessage", errorMessage);
            }
            return result;
        }
    }

    // 创建一个ConcurrentHashMap来存储进行中的导入任务统计信息
    private static final ConcurrentHashMap<Integer, ImportStats> importTaskStats = new ConcurrentHashMap<>();

    /**
     * 获取导入任务的统计信息
     * @param taskPackageId 任务包ID
     * @return 统计信息，如果不存在则返回null
     */
    @Override
    public Map<String, Object> getImportStats(Integer taskPackageId) {
        ImportStats stats = importTaskStats.get(taskPackageId);
        if (stats != null) {
            return stats.toMap();
        }
        return null;
    }

    /**
     * 批量导入资源
     *
     * @param resourceInfos 资源VO列表
     * @param taskPackageId 任务包ID
     * @param accountName 账户名称
     * @return 导入成功的数量
     * @throws Exception 处理过程中的异常
     */
    @Override
    public int batchImportResource(List<ResourceInfoVO> resourceInfos, Integer taskPackageId, String accountName) throws Exception {
        log.info("开始批量导入资源，批次大小: {}, 任务包ID: {}, 账户: {}, 数据条数: {}", 
                batchSize, taskPackageId, accountName, resourceInfos.size());
        
        long startTime = System.currentTimeMillis();
        
        // 初始化统计对象
        ImportStats stats = new ImportStats(batchSize);
        importTaskStats.put(taskPackageId, stats);
        stats.startImport(resourceInfos.size());
        
        // 将资源列表分成多个批次处理
        List<List<ResourceInfoVO>> batches = new ArrayList<>();
        int batchCount = (resourceInfos.size() + batchSize - 1) / batchSize;
        for (int i = 0; i < batchCount; i++) {
            int fromIndex = i * batchSize;
            int toIndex = Math.min(fromIndex + batchSize, resourceInfos.size());
            batches.add(resourceInfos.subList(fromIndex, toIndex));
        }
        log.info("资源数据已分为 {} 个批次处理", batches.size());
        
        final AtomicInteger successCount = new AtomicInteger(0);
        final CountDownLatch latch = new CountDownLatch(batches.size());
        final StringBuilder errorMessages = new StringBuilder();
        
        // 创建线程池
        final ExecutorService executor = Executors.newFixedThreadPool(Math.min(threadPoolSize, batches.size()));
        
        // 创建事务模板
        final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        
        try {
            // 处理每个批次
            for (int i = 0; i < batches.size(); i++) {
                final List<ResourceInfoVO> batch = batches.get(i);
                final int batchIndex = i;
                
                executor.submit(() -> {
                    try {
                        log.info("处理批次 {}/{}, 大小: {}", batchIndex + 1, batches.size(), batch.size());
                        
                        // 使用事务模板执行每个批次
                        Integer importedCount = transactionTemplate.execute(status -> {
                            try {
                                return processBatchOptimized(batch, taskPackageId, accountName);
                            } catch (Exception e) {
                                log.error("批次 {} 处理失败: {}", batchIndex + 1, e.getMessage(), e);
                                synchronized (errorMessages) {
                                    errorMessages.append("批次 ").append(batchIndex + 1)
                                            .append(" 错误: ").append(e.getMessage()).append("; ");
                                }
                                status.setRollbackOnly();
                                return 0;
                            }
                        });
                        
                        // 更新成功计数
                        if (importedCount != null) {
                            successCount.addAndGet(importedCount);
                            stats.updateProgress(batch.size(), importedCount);
                            log.info("批次 {}/{} 完成，成功导入 {} 条记录", 
                                    batchIndex + 1, batches.size(), importedCount);
                        }
                    } catch (Exception e) {
                        log.error("批次 {} 执行异常: {}", batchIndex + 1, e.getMessage(), e);
                        synchronized (errorMessages) {
                            errorMessages.append("批次 ").append(batchIndex + 1)
                                    .append(" 异常: ").append(e.getMessage()).append("; ");
                        }
                    } finally {
                        latch.countDown();
                    }
                });
            }
            
            // 等待所有批次处理完成
            try {
                latch.await(30, TimeUnit.MINUTES);  // 设置最长等待时间
            } catch (InterruptedException e) {
                log.error("等待批处理完成时被中断: {}", e.getMessage());
                errorMessages.append("处理被中断: ").append(e.getMessage());
            }
            
            // 关闭线程池
            executor.shutdown();
            
            // 完成导入
            stats.completeImport();
            
            // 检查是否有错误
            if (errorMessages.length() > 0) {
                stats.setPartialError(errorMessages.toString());
                log.warn("批量导入部分失败: {}", errorMessages);
            }
            
            long endTime = System.currentTimeMillis();
            log.info("批量导入资源完成, 任务包ID: {}, 成功导入: {}/{}, 耗时: {} ms", 
                    taskPackageId, successCount.get(), resourceInfos.size(), (endTime - startTime));
            
            return successCount.get();
        } catch (Exception e) {
            log.error("批量导入资源失败: {}", e.getMessage(), e);
            stats.setError("导入失败: " + e.getMessage());
            throw e;
        }
    }
    
    /**
     * 优化的批处理方法：
     * 1. 直接插入资源数据，利用数据库唯一索引去重
     * 2. 为新插入的和已存在的资源关联任务包
     * 3. 每个批次使用独立事务
     */
    @Transactional
    protected int processBatchOptimized(List<ResourceInfoVO> batchVOs, Integer taskPackageId, String accountName) {
        if (batchVOs == null || batchVOs.isEmpty()) {
            return 0;
        }
        
        try {
            // 1. 提取和验证手机号码
            List<String> validPhones = new ArrayList<>();
            Map<String, ResourceInfoVO> phoneToResourceMap = new HashMap<>();
            
            for (ResourceInfoVO resourceInfoVO : batchVOs) {
                if (StringUtils.isNotBlank(resourceInfoVO.getPhoneA())) {
                    String phoneNumber = resourceInfoVO.getPhoneA().trim();
                    if (isValidPhoneNumber(phoneNumber)) {
                        validPhones.add(phoneNumber);
                        phoneToResourceMap.put(phoneNumber, resourceInfoVO);
                    } else {
                        log.warn("忽略无效的手机号码: {}", phoneNumber);
                    }
                }
            }
            
            if (validPhones.isEmpty()) {
                log.warn("批次中没有有效的手机号码");
                return 0;
            }
            
            // 2. 创建资源，使用INSERT IGNORE，利用数据库的唯一索引自动去重
            List<ResourceInfo> resourcesForInsert = new ArrayList<>();
            for (String phone : validPhones) {
                ResourceInfoVO vo = phoneToResourceMap.get(phone);
                ResourceInfo resource = new ResourceInfo();
                resource.setResourceName(vo.getResourceName());
                resource.setPhoneA(phone);
                resource.setPhoneB(vo.getPhoneB());
                resource.setLinkman(vo.getLinkman());
                resource.setRemark(vo.getRemark());
                resource.setCreateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                resource.setAccountName(accountName);
                resourcesForInsert.add(resource);
            }
            
            // 3. 批量插入资源 (使用INSERT IGNORE，数据库有唯一索引自动去重)
            int insertedCount = 0;
            if (!resourcesForInsert.isEmpty()) {
                insertedCount = resourceInfoMapper.batchAddResourceInfo(resourcesForInsert);
                log.info("处理批次 {} 条记录，成功插入 {} 条新资源", resourcesForInsert.size(), insertedCount);
            }
            
            // 4. 关联资源到任务包 - 直接通过phoneA关联，不需要查询resourceId
            // 使用addTaskPackageResourceByPhones方法关联，这个方法会直接在数据库层面通过phoneA查找资源ID并插入关联
            if (!validPhones.isEmpty()) {
                int associatedCount = taskPackageResourceMapper.addTaskPackageResourceByPhones(validPhones, taskPackageId);
                log.info("为任务包 {} 关联了 {} 个资源", taskPackageId, associatedCount);
            }

            // 5. 返回处理的数量 - 这里返回关联成功的数量而不是插入的数量，避免重复计算
            return insertedCount;

        } catch (Exception e) {
            log.error("处理批次失败: {}", e.getMessage(), e);
            throw new RuntimeException("处理批次异常: " + e.getMessage(), e);
        }
    }
    
    /**
     * 验证手机号码格式
     */
    private boolean isValidPhoneNumber(String phoneNumber) {
        if (StringUtils.isBlank(phoneNumber)) {
            return false;
        }
        // 简单验证手机号码格式，可根据实际需求调整
        return phoneNumber.matches("^1[3-9]\\d{9}$");
    }

    /**
     * 创建大型任务包
     *
     * @param mergedFilename Excel文件路径
     * @param taskPackageVO 任务包信息
     * @return 包含处理结果和统计信息的Map
     * @throws Exception 处理过程中的异常
     */
    @Override
    @Transactional
    public String createLargeTaskPackage(String mergedFilename, TaskPackageVO taskPackageVO) throws Exception {
        long startTime = System.currentTimeMillis();
        
        // 获取传入的任务包ID (控制器层已经创建了任务包)
        final Integer taskPackageId = taskPackageVO.getTaskPackageId();
        if (taskPackageId == null) {
            log.error("任务包ID为空，请确保已创建任务包并设置ID");
            return "任务包ID为空";
        }
        
        log.info("开始处理任务包资源: {}, ID: {}, 文件路径: {}", 
                taskPackageVO.getTaskPackageName(), 
                taskPackageId,
                mergedFilename);

        // 获取任务包ID，用于后续的统计和日志
        final AtomicInteger taskPackageIdHolder = new AtomicInteger(taskPackageId);
        
        try {
            // 跳过创建任务包的步骤，直接使用传入的ID
            // 检查导入统计对象是否存在
            ImportStats stats = importTaskStats.get(taskPackageId);
            if (stats == null) {
                stats = new ImportStats(batchSize);
                importTaskStats.put(taskPackageId, stats);
                log.info("为任务包创建统计对象: {}", taskPackageId);
            }
            
            // 2. 检查Excel文件
            java.io.File excelFile = new java.io.File(mergedFilename);
            if (!excelFile.exists()) {
                String errorMsg = "Excel文件不存在: " + mergedFilename;
                log.error(errorMsg);
                stats.setError(errorMsg);
                return errorMsg;
            }
            
            log.info("文件存在，大小: {} 字节", excelFile.length());
            
            // 3. 设置字段映射
            Map<String, String> fieldMapping = new HashMap<>();
            fieldMapping.put("客户名称", "resourceName");
            fieldMapping.put("电话A", "phoneA");
            fieldMapping.put("电话B", "phoneB");
            fieldMapping.put("联系人姓名", "linkman");
            fieldMapping.put("备注", "remark");
            
            // 4. 创建事务模板
            TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
            transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
            // 增加超时时间到5分钟，处理大文件可能需要更长时间
            transactionTemplate.setTimeout(300);
            
            // 5. 统计处理信息
            final AtomicInteger totalProcessed = new AtomicInteger(0);
            final AtomicInteger totalImported = new AtomicInteger(0);
            final AtomicInteger processedBatches = new AtomicInteger(0);
            final AtomicInteger failedBatches = new AtomicInteger(0);
            final StringBuilder errorDetails = new StringBuilder();
            
            // 6. 计算文件总行数（用于进度计算）
            int totalRows = countExcelRows(excelFile, fieldMapping);
            log.info("文件总行数: {}", totalRows);
            final ImportStats finalStats = stats;
            finalStats.startImport(totalRows);
            
            try {
                // 使用流式处理Excel文件
                com.easygem.common.utils.ExcelUtils.processExcelInBatches(
                    excelFile, 
                    ResourceInfoVO.class, 
                    fieldMapping, 
                    (batch, isLast) -> {
                        if (!batch.isEmpty()) {
                            try {
                                int batchNumber = processedBatches.incrementAndGet();
                                log.info("处理批次 {}, 任务包ID: {}, 批次大小: {}", batchNumber, taskPackageId, batch.size());
                                
                                // 记录总处理数
                                totalProcessed.addAndGet(batch.size());
                                
                                // 批量导入资源
                                int importedCount = transactionTemplate.execute(status -> {
                                    try {
                                        return processBatchOptimized(batch, taskPackageId, taskPackageVO.getAccountName());
                                    } catch (Exception e) {
                                        status.setRollbackOnly();
                                        log.error("批次导入失败，任务包ID: {}, 批次: {}, 错误: {}", 
                                                taskPackageId, batchNumber, e.getMessage(), e);
                                        // 记录详细错误信息
                                        synchronized (errorDetails) {
                                            errorDetails.append("批次").append(batchNumber)
                                                .append("(行").append(totalProcessed.get() - batch.size() + 1)
                                                .append("-").append(totalProcessed.get())
                                                .append(")错误: ").append(e.getMessage()).append("\n");
                                        }
                                        failedBatches.incrementAndGet();
                                        throw new RuntimeException("批次处理失败: " + e.getMessage(), e);
                                    }
                                });
                                
                                totalImported.addAndGet(importedCount);
                                
                                // 更新进度统计
                                finalStats.updateProgress(batch.size(), importedCount);
                                
                                log.info("批次{}处理完成，任务包ID: {}，导入: {}/{} 条记录，当前总进度: {}/{}", 
                                        batchNumber, taskPackageId, importedCount, batch.size(), 
                                        totalImported.get(), totalProcessed.get());
                                
                                // 每10个批次记录一次摘要
                                if (batchNumber % 10 == 0) {
                                    log.info("进度摘要 - 任务包ID: {}, 总行数: {}, 已处理: {}, 已导入: {}, 进度: {}%, 失败批次: {}", 
                                            taskPackageId, totalRows, totalProcessed.get(), totalImported.get(), 
                                            totalRows > 0 ? (totalProcessed.get() * 100 / totalRows) : 0,
                                            failedBatches.get());
                                }
                            } catch (Exception e) {
                                log.error("批次处理异常，任务包ID: {}, 总处理行数: {}, 错误: {}", 
                                        taskPackageId, totalProcessed.get(), e.getMessage(), e);
                                failedBatches.incrementAndGet();
                                
                                // 记录详细错误信息
                                synchronized (errorDetails) {
                                    errorDetails.append("处理异常(行").append(totalProcessed.get() - batch.size() + 1)
                                        .append("-").append(totalProcessed.get())
                                        .append("): ").append(e.getMessage()).append("\n");
                                }
                            }
                        }
                    }, 
                    batchSize
                );
                
                // 7. 尝试删除临时文件
                try {
                    if (excelFile.exists() && !excelFile.delete()) {
                        log.warn("无法删除临时文件: {}", excelFile.getAbsolutePath());
                    }
                } catch (Exception e) {
                    log.warn("删除临时文件出错: {}", e.getMessage());
                }
                
                // 8. 导入完成，更新统计信息
                finalStats.completeImport();
                if (failedBatches.get() > 0) {
                    String errorSummary = "部分批次处理失败 (" + failedBatches.get() + "批次), 详情: " + 
                        (errorDetails.length() > 1000 ? errorDetails.substring(0, 997) + "..." : errorDetails.toString());
                    finalStats.setPartialError(errorSummary);
                    log.warn(errorSummary);
                }
                
                // 9. 检查导入结果
                long endTime = System.currentTimeMillis();
                log.info("大型任务包创建完成，ID: {}，总行数: {}, 处理: {} 条记录，成功导入: {} 条记录，失败批次: {}，耗时: {} ms", 
                        taskPackageId, totalRows, totalProcessed.get(), totalImported.get(), 
                        failedBatches.get(), (endTime - startTime));
                
                if (totalImported.get() == 0) {
                    if (totalProcessed.get() > 0) {
                        // 有处理数据但无导入，可能全是重复数据
                        return "您导入的数据与已有资源重复，操作已结束";
                    } else {
                        // 无处理数据，可能文件为空
                        return "导入文件无有效数据";
                    }
                }
                
                if (failedBatches.get() > 0) {
                    return "部分批次导入失败，已导入 " + totalImported.get() + "/" + totalProcessed.get() + " 条记录";
                }
                
                return "success";
            } catch (Exception e) {
                String errorMsg = "大文件导入失败: " + e.getMessage();
                finalStats.setError(errorMsg);
                log.error("大型任务包创建过程中发生错误: {}", e.getMessage(), e);
                throw new RuntimeException(errorMsg, e);
            }
        } catch (Exception e) {
            log.error("创建任务包失败: {}", e.getMessage(), e);
            // 如果已经创建了任务包和统计对象，记录错误
            if (taskPackageIdHolder.get() > 0) {
                if (importTaskStats.containsKey(taskPackageIdHolder.get())) {
                    ImportStats stats = importTaskStats.get(taskPackageIdHolder.get());
                    stats.setError("处理失败: " + e.getMessage());
                }
            }
            return "创建任务包失败: " + e.getMessage();
        }
    }
    
    /**
     * 计算Excel文件总行数
     * @param excelFile Excel文件
     * @param fieldMapping 字段映射
     * @return 总行数
     */
    private int countExcelRows(java.io.File excelFile, Map<String, String> fieldMapping) {
        try {
            final AtomicInteger rowCount = new AtomicInteger(0);
            
            com.easygem.common.utils.ExcelUtils.processExcelInBatches(
                excelFile,
                ResourceInfoVO.class,
                fieldMapping,
                (batch, isLast) -> rowCount.addAndGet(batch.size()),
                1000 // 使用较大的批次大小只是为了计数
            );
            
            return rowCount.get();
        } catch (Exception e) {
            log.error("计算Excel行数失败: {}", e.getMessage(), e);
            return 0;
        }
    }

    @Override
    public List<ResourceInfoVO> sqlDeWeight(List<ResourceInfoVO> list) {
        // 现在我们依赖数据库的唯一索引来去重，所以这个方法简化为仅返回输入列表
        log.info("使用数据库唯一索引进行去重，无需额外处理");
        return list;
    }
}

